/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Engine2D.network;

import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;

/**
 *
 * @param <E> 
 * @author Garys
 */
public class ServerConnection<E extends Session> implements Connection<ServerSocket> {

    private ServerSocket serverSocket = null;
    private int port;
    private ArrayList<E> clients = null;
    private SessionCreator<E> serverLinkCreator = null;

    /**
     * 
     * @param port
     */
    public ServerConnection(int port) {
        this.port = port;
        clients = new ArrayList<E>();
    }

    /**
     * 
     * @return
     */
    @Override
    public boolean connect() {

        try {
            serverSocket = new ServerSocket(port);
            return true;
        } catch (IOException ex) {
            serverSocket = null;
            return false;
        }

    }

    /**
     * 
     * @return
     */
    public E acceptConnection() {

        try {
            Socket socket = serverSocket.accept();
            return addLink(socket);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 
     * @return
     */
    @Override
    public boolean disconnect() {
        if (!isConnected()) {
            return true;
        }

        disconnectClients();
        try {
            closeServerSocket();
            return true;
        } catch (Exception e) {


            if (serverSocket != null && serverSocket.isClosed()) {
                serverSocket = null;
            }
            return false;
        }
    }

    /**
     * 
     * @return
     */
    @Override
    public boolean isConnected() {
        if (serverSocket != null) {
            return !serverSocket.isClosed();
        }
        return false;
    }

    /**
     * 
     * @return
     */
    @Override
    public ServerSocket getSocket() {
        return serverSocket;
    }

    /**
     * 
     * @return
     */
    @Override
    public InetAddress getLocalIP() {
        if (isConnected()) {
            return getSocket().getInetAddress();
        }
        return null;
    }

    /**
     * 
     * @return
     */
    @Override
    public InetAddress getRemoteIP() {
        return null;
    }

    /**
     * 
     * @return
     */
    @Override
    public boolean isServer() {
        return true;
    }

    /**
     * 
     */
    public void disconnectClients() {

        List clientsList = Collections.synchronizedList(clients);

        synchronized (clientsList) {
            //Close all connections
            for (int i = 0; i < getLinkCount(); i++) {
                getLink(i).disconnect();
            }

            //Remove all client links from the list and create a new empty one
            clients.clear();
            clients = new ArrayList<E>();
        }
    }

    /**
     * 
     * @param link
     * @return
     */
    public boolean disconnectClient(E link) {

        if (link.disconnect()) {
            return clients.remove(link);
        }
        return false;

    }

    private void closeServerSocket() throws IOException {
        if (serverSocket != null) {

            serverSocket.close();
            serverSocket = null;

        }
    }

    /**
     * 
     * @return
     */
    public int getLinkCount() {
        return clients.size();
    }

    /**
     * 
     * @param index
     * @return
     */
    public E getLink(int index) {
        return clients.get(index);
    }

    /**
     * 
     * @return
     */
    public ArrayList<E> getLinks() {
        return clients;
    }

    private E addLink(Socket socket) {
        if (getServerLinkCreator() == null) {
            throw new RuntimeException("ServerLinkCreator = null");
        }

        E client = null;

        try {
            client = getServerLinkCreator().create(socket);
        } catch (Exception e) {
            client = null;
            return null;
        }

        return clients.add(client) ? client : null;


    }

    /**
     * 
     * @return
     */
    public SessionCreator<E> getServerLinkCreator() {
        return serverLinkCreator;
    }

    /**
     * 
     * @param linkCreator
     */
    public void setServerLinkCreator(SessionCreator<E> linkCreator) {
        this.serverLinkCreator = linkCreator;
    }

    /**
     * 
     * @return
     */
    public int getLocalPort() {
        if (getSocket() != null) {
            return getSocket().getLocalPort();
        }
        return port;

    }

    /**
     * 
     * @return
     */
    public int getRemotePort() {
        return -1;
    }
    
    /**
     * 
     * @param args
     */
    public static void main(String[] args) {
        final ServerConnection<Session> serverConnection = new ServerConnection<Session>(8000);
        
        serverConnection.setServerLinkCreator(new SessionCreator<Session>()
        {
            @Override
			public Session create(Socket socket) throws IOException
			{
				Session clientLink = new Session(socket);
				
				return clientLink;
			}
        
        }
                
                );
        boolean h = serverConnection.connect();
        Thread acceptor = new Thread()
        {
            @Override
            public void run()
            {
                Session session = null;
                while (serverConnection.isConnected()) { 
                    
                    if((session = serverConnection.acceptConnection() ) != null)
                    {
                        //session.getListener().start();
                    }
                    try {
                        Thread.sleep(30);
                    } catch (InterruptedException ex) {
                        Logger.getLogger(ServerConnection.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
        };
        
        acceptor.start();
        
        int number = -1;
        while (true) { 
            
            if (number < serverConnection.getLinkCount()) {
                System.out.println(serverConnection.getLinkCount());
                number++;
            }
            try {
                Thread.sleep(20);
            } catch (InterruptedException ex) {
                Logger.getLogger(ServerConnection.class.getName()).log(Level.SEVERE, null, ex);
            }
            
        }
    }
}
